package com.ikun.admin.modules.auth.filter;

import com.alibaba.fastjson.JSON;
import com.ikun.admin.modules.auth.eneity.SecurityUser;
import com.ikun.admin.modules.auth.utils.ResponseCode;
import com.ikun.admin.modules.auth.utils.ResponseUtils;
import com.ikun.common.entity.SecurityUserInfo;
import com.ikun.common.manager.RedisManager;
import com.ikun.common.result.Result;
import com.ikun.starters.jwt.ITokenService;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * @author ximeng
 * @title: LoginFilter 登录拦截器
 * @date 2023/10/8  15:33
 */
public class LoginFilter extends UsernamePasswordAuthenticationFilter {

    private ITokenService tokenService;

    private AuthenticationManager authenticationManager;

    private RedisManager redisManager;

    public LoginFilter(AuthenticationManager authenticationManager, ITokenService tokenService, RedisManager redisManager) {
        this.authenticationManager = authenticationManager;
        this.tokenService = tokenService;
        this.redisManager = redisManager;
        //指定请求的方法(登陆只允许POST请求)
        this.setPostOnly(true);
        //设置登陆的地址
        this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher(
                "/sys/user/login", "POST"
        ));
    }

    /**
     * 从请求中获取数据
     *
     * @param request
     * @return
     * @throws IOException
     */
    private static SecurityUserInfo getUserInfoFromReq(HttpServletRequest request) throws IOException {
        ServletInputStream inputStream = request.getInputStream();
        byte[] buf = new byte[2048];
        int readLength = inputStream.read(buf);
        String dataJsonStr = new String(buf, 0, readLength, "UTF-8");
        SecurityUserInfo securityUserInfo = JSON.parseObject(dataJsonStr, SecurityUserInfo.class);
        return securityUserInfo;
    }

    /**
     * 登陆的处理方法
     *
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        //登录处理
        SecurityUserInfo securityUserInfo;
        try {
            securityUserInfo = getUserInfoFromReq(request);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if (Objects.isNull(securityUserInfo) || Objects.isNull(securityUserInfo.getUsername()) || Objects.isNull(securityUserInfo.getPassword())) {
            try {
                unsuccessfulAuthentication(request, response, null);
            } catch (IOException e) {
                throw new RuntimeException(e);
            } catch (ServletException e) {
                throw new RuntimeException(e);
            }
        }
        //加载用户信息
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(JSON.toJSONString(securityUserInfo), securityUserInfo.getPassword());

        //认证器进行认证
        return this.authenticationManager.authenticate(usernamePasswordAuthenticationToken);
    }

    /**
     * 登陆成功之后的处理方法
     *
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        // 更新 SecurityContextHolder
        SecurityContextHolder.getContext().setAuthentication(authResult);

        SecurityUser securityUser = (SecurityUser) authResult.getPrincipal();

        List<String> roleAndPerms = securityUser.getRoleAndPerms();
        securityUser.setRoleAndPerms(roleAndPerms);

        Result<SecurityUserInfo> success = new Result<SecurityUserInfo>().success(securityUser.getUserInfo());
        ResponseUtils.sendResponse(response, success);
    }

    /**
     * 登陆失败之后的处理方法
     *
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        Result<String> error = new Result<String>().error(ResponseCode.LOGIN_FAIL.getCode(), ResponseCode.LOGIN_FAIL.getMessage());
        ResponseUtils.sendResponse(response, error);
    }
}
